package com.koron.configure.workflow.controller;

import com.alibaba.fastjson.JSONObject;
import com.koron.bean.base.Response;
import com.koron.bean.workflow.SubmitterParam;
import com.koron.bean.workflow.WorkflowCustomUser;
import com.koron.bean.workflow.WorkflowUser;
import com.koron.configure.feign.operation.ShiftFeign;
import com.koron.configure.feign.operation.dto.ChangeShiftDTO;
import com.koron.configure.feign.operation.dto.ClassesShiftDTO;
import com.koron.configure.feign.operation.query.ShiftRecordQuery;
import com.koron.configure.workflow.bean.enums.DeptMarkEnum;
import com.koron.configure.workflow.service.impl.StaffHelper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 通用流程插件接口
 *
 * 对于submitter，如果返回的是[]，则报错，返回null，没问题
 * 对于handler，如果返回的是[]，则报错，返回null，前端会弹出选所有人的组件
 *
 *
 * @author jzp
 * @since 2022/6/15
 */
@Slf4j
@Api(tags = "通用流程插件")
@RequestMapping("pluginWorkflow")
@RestController
public class PluginWorkflowController {

    private static final String submitter = "submitter";
    private static final String handler = "handler";

    @Autowired
    private StaffHelper staffHelper;

    @Autowired
    private ShiftFeign shiftFeign;

    /**
     * [岗位的人员 ∪ 岗位的人员] ∩ [部门的人员 ∪ 部门的人员] ∪ [人员]
     * 部门标识：com.koron.configure.workflow.bean.enums.DeptMarkEnum
     * 部门表达式参考oneToOne
     */
    @ApiOperation(value = "默认插件", notes = "[岗位的人员 ∪ 岗位的人员] ∩ [部门的人员 ∪ 部门的人员] ∪ [人员]")
    @PostMapping(value = {handler, submitter})
    public Response<List<WorkflowUser>> handler(@RequestBody SubmitterParam submitterParam, @RequestParam(required = false) String condition) {
        if (condition != null && !allowCondition(submitterParam.getFormVariables(), condition)) {
            return Response.ok(null);
        }
        // 解析部门表达式
        List<WorkflowUser> users = submitterParam.getUsers();
        if (!CollectionUtils.isEmpty(users)) {
            // 拿到所有的部门
            List<WorkflowUser> deptList = users.stream().filter(e -> e.getType() == 1).collect(Collectors.toList());
            List<WorkflowUser> newDeptList = new ArrayList<>();
            for (WorkflowUser workflowUser : deptList) {
                Function<String, List<WorkflowUser>> function = DeptMarkEnum.getByMark(workflowUser.getName());
                List<WorkflowUser> apply = function.apply(submitterParam.getFormVariables());
                if (apply == null) {
                    // null表示没有变量，正常使用即可
                    newDeptList.add(workflowUser);
                } else {
                    // 有变量
                    newDeptList.addAll(apply);
                }
            }
            // 删掉原来的dept信息，重新构建
            users.removeIf(e -> e.getType() == 1);
            users.addAll(newDeptList);
        }

        return Response.ok(staffHelper.mixed(submitterParam.getUsers()));
    }

    /**
     * 部门和岗位一一对应
     * 部门标识：com.koron.configure.workflow.bean.enums.DeptMarkEnum
     * 组织标识：
     *  -- 通用标识
     *    na                   空组织，以岗位为准
     *    deptId               取deptId字段，相当于$deptId
     *    createByOrg          取创建人所在单位，相当于$createBy:ORG
     *    createByDept         取创建人所在部门，相当于$createBy:DEPT
     *  -- 变量标识
     *    $deptManage          则会拿deptManage字段的值作为部门id
     *    $deptManage&         则会拿deptManage字段的值作为单位id，然后取其部门
     *    $deptManage^         则会拿deptManage字段的值作为单位id，然后取其上一级单位
     *    $createBy:ORG        则会拿create字段的值作为人员，然后取其单位
     *    $createBy:DEPT       则会拿createBy字段的值作为人员，然后取其部门
     */
    @ApiOperation(value = "部门和岗位一一对应", notes = "部门标识：空部门na;\n业务数据的deptId字段对应的部门deptId;\n创建人所在部门createByOrg;\n自定义标识：如$deptManager，则会拿deptManager字段的值作为部门id；如$author:DEPT，则会拿author字段的值作为人员，然后取其部门")
    @PostMapping(value = {"oneToOne/"+handler, "oneToOne/"+submitter})
    public Response<List<WorkflowUser>> oneToOneSubmitter(@RequestBody SubmitterParam submitterParam, @RequestParam(required = false) String condition) {
        if (condition != null && !allowCondition(submitterParam.getFormVariables(), condition)) {
            return Response.ok(null);
        }
        List<WorkflowUser> users = submitterParam.getUsers();
        Assert.state(!CollectionUtils.isEmpty(users), "一对一配置不允许配置信息为空，请检查流程配置");
        // 拿到所有的部门
        List<WorkflowUser> deptList = users.stream().filter(e -> e.getType() == 1).collect(Collectors.toList());
        // 拿到所有的岗位
        List<WorkflowUser> postList = users.stream().filter(e -> e.getType() == 2).collect(Collectors.toList());
        Assert.state(CollectionUtils.size(deptList)==CollectionUtils.size(postList), "一对一配置岗位和部门需一对一，空可以用na标识，请检查流程配置");
        List<WorkflowUser> result = new ArrayList<>();
        for (int i = 0; i < CollectionUtils.size(deptList); i++) {
            WorkflowUser workflowUser = deptList.get(i);
            Function<String, List<WorkflowUser>> function = DeptMarkEnum.getByMark(workflowUser.getName());
            List<WorkflowUser> apply = function.apply(submitterParam.getFormVariables());
            if (apply == null) {
                // null表示没有变量，正常使用即可
                result.addAll(staffHelper.mixed(Arrays.asList(postList.get(i), workflowUser)));
            } else {
                // 有变量
                apply.add(postList.get(i));
                result.addAll(staffHelper.mixed(apply));
            }
        }
        return Response.ok(result);
    }

    /**
     * 部门和岗位一一对应，人员作为条件
     * 部门标识：com.koron.configure.workflow.bean.enums.DeptMarkEnum
     * 组织标识：参考oneToOne
     * 人员条件，如 #price>100000
     */
    @ApiOperation(value = "部门和岗位一一对应，人员作为条件", notes = "部门标识：空部门na;\n业务数据的deptId字段对应的部门deptId;\n创建人所在部门createByOrg;\n自定义标识：如$deptManager，则会拿deptManager字段的值作为部门id；如$author:DEPT，则会拿author字段的值作为人员，然后取其部门")
    @PostMapping(value = {"oneToOnePredict/"+handler, "oneToOnePredict/"+submitter})
    public Response<List<WorkflowUser>> oneToOnePredictSubmitter(@RequestBody SubmitterParam submitterParam, @RequestParam(required = false) String condition) {
        if (condition != null && !allowCondition(submitterParam.getFormVariables(), condition)) {
            return Response.ok(null);
        }
        List<WorkflowUser> users = submitterParam.getUsers();
        Assert.state(!CollectionUtils.isEmpty(users), "一对一配置不允许配置信息为空，请检查流程配置");
        // 拿到所有的条件
        List<WorkflowUser> predictList = users.stream().filter(e -> e.getType() == 0).collect(Collectors.toList());
        // 拿到所有的部门
        List<WorkflowUser> deptList = users.stream().filter(e -> e.getType() == 1).collect(Collectors.toList());
        // 拿到所有的岗位
        List<WorkflowUser> postList = users.stream().filter(e -> e.getType() == 2).collect(Collectors.toList());
        Assert.state(CollectionUtils.size(deptList) == CollectionUtils.size(postList) && CollectionUtils.size(deptList) == CollectionUtils.size(predictList), "一对一配置岗位和部门需一对一，空可以用na标识，请检查流程配置");
        List<WorkflowUser> result = new ArrayList<>();
        // 定义上下文
        EvaluationContext context = new StandardEvaluationContext();
        JSONObject jsonObject = JSONObject.parseObject(submitterParam.getFormVariables());
        // 将FormVariables中的值都填入上下文中
        jsonObject.forEach(context::setVariable);
        // 定义解析表达式
        ExpressionParser expressionParser = new SpelExpressionParser();
        for (int i = 0; i < CollectionUtils.size(deptList); i++) {
            // 忽略predict的值
            WorkflowUser predict = predictList.get(i);
            // 根据传入的字符串构建表达式
            Expression expression = expressionParser.parseExpression(predict.getCode().replace("$", "#"));
            // 解析，并获取boolean值
            Boolean value = expression.getValue(context,Boolean.class);
            if (Boolean.FALSE.equals(value)) {
                continue;
            }
            WorkflowUser workflowUser = deptList.get(i);
            Function<String, List<WorkflowUser>> function = DeptMarkEnum.getByMark(workflowUser.getCode());
            List<WorkflowUser> apply = function.apply(submitterParam.getFormVariables());
            if (apply == null) {
                // null表示没有变量，正常使用即可
                result.addAll(staffHelper.mixed(Arrays.asList(postList.get(i), workflowUser)));
            } else {
                // 有变量
                apply.add(postList.get(i));
                result.addAll(staffHelper.mixed(apply));
            }
        }
        // 如果返回的是[]，则报错，返回null，没问题
        return Response.ok(result.isEmpty() ? null : result);
    }

    /**
     * 选择创建人所在部门的人
     */
    @ApiOperation(value = "选择创建人所在部门的人")
    @PostMapping(value = {"creatorOrg/"+handler, "creatorOrg/"+submitter})
    public Response<List<WorkflowUser>> creatorOrgHandler(@RequestBody SubmitterParam submitterParam, @RequestParam(required = false) String condition) {
        // 符合条件则跳过
        if (condition != null && !allowCondition(submitterParam.getFormVariables(), condition)) {
            return Response.ok(null);
        }
        JSONObject jsonObject = JSONObject.parseObject(submitterParam.getFormVariables());
        String createBy = jsonObject.getString("createBy");
        // 2. 获取编制人所在部门
        List<WorkflowUser> org = staffHelper.getDeptByAccount(createBy);
        List<WorkflowUser> users = submitterParam.getUsers();
        if (CollectionUtils.isEmpty(users)) {
            submitterParam.setUsers(org);
        } else {
            submitterParam.getUsers().addAll(org);
        }
        return Response.ok(staffHelper.mixed(submitterParam.getUsers()));
    }

    /**
     * $会转化为#，原因为url不支持#
     * ?condition=$price>100000
     */
    private boolean allowCondition(String formVariables, String condition) {
        // 定义上下文
        EvaluationContext context = new StandardEvaluationContext();
        JSONObject jsonObject = JSONObject.parseObject(formVariables);
        // 将FormVariables中的值都填入上下文中
        jsonObject.forEach(context::setVariable);
        // 定义解析表达式
        ExpressionParser expressionParser = new SpelExpressionParser();
        // 根据传入的字符串构建表达式
        Expression expression = expressionParser.parseExpression(condition.replace("$", "#"));
        // 解析，并获取boolean值
        return Boolean.TRUE.equals(expression.getValue(context, Boolean.class));
    }

    /**
     * [岗位的人员 ∪ 岗位的人员] ∩ [部门的人员 ∪ 部门的人员] ∪ [人员]
     * 部门标识：com.koron.configure.workflow.bean.enums.DeptMarkEnum
     * 部门表达式参考oneToOne
     */
    @ApiOperation(value = "自定义选人配置插件", notes = "[岗位的人员 ∪ 岗位的人员] ∩ [部门的人员 ∪ 部门的人员] ∪ [人员]")
    @PostMapping(value = {"customUserConfig/"+handler, "customUserConfig/"+submitter})
    public Response<List<WorkflowUser>> customUserConfig(@RequestBody SubmitterParam submitterParam, @RequestParam(required = false) String condition) {
        if (condition != null && !allowCondition(submitterParam.getFormVariables(), condition)) {
            return Response.ok(null);
        }
        List<WorkflowCustomUser> result = new ArrayList<>();
        List<WorkflowUser> mixed = null;
        // 解析部门表达式
        List<WorkflowUser> users = submitterParam.getUsers();
        if (!CollectionUtils.isEmpty(users)) {
            // 拿到所有的部门
            List<WorkflowUser> deptList = users.stream().filter(e -> e.getType() == 1).collect(Collectors.toList());
            List<WorkflowUser> newDeptList = new ArrayList<>();
            for (WorkflowUser workflowUser : deptList) {
                Function<String, List<WorkflowUser>> function = DeptMarkEnum.getByMark(workflowUser.getName());
                List<WorkflowUser> apply = function.apply(submitterParam.getFormVariables());
                if (apply == null) {
                    // null表示没有变量，正常使用即可
                    newDeptList.add(workflowUser);
                } else {
                    // 有变量
                    newDeptList.addAll(apply);
                }
            }
            // 删掉原来的dept信息，重新构建
            users.removeIf(e -> e.getType() == 1);
            users.addAll(newDeptList);
            // 获取deptId
            String deptId = newDeptList.get(0).getCode();
            List<WorkflowCustomUser> customUserList = submitterParam.getCustomUsers();
            // 如果配置了人员则从配置人员中筛选
            if (CollectionUtils.isNotEmpty(customUserList)){
                mixed = new ArrayList<>();
                result =  customUserList.stream().filter(c -> c.getDeptId().equals(deptId)).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(result)){
                    for (WorkflowCustomUser workflowCustomUser : result) {
                        mixed.add(new WorkflowUser(workflowCustomUser.getAccount(),workflowCustomUser.getName()));
                    }
                }else {
                    // 否则查找该岗位默认配置的人员
                    List<WorkflowCustomUser> defaultUserList = customUserList.stream().filter(c -> c.getDefaultUser() == 1 ).collect(Collectors.toList());
                    for (WorkflowCustomUser workflowCustomUser : defaultUserList) {
                        mixed.add(new WorkflowUser(workflowCustomUser.getAccount(),workflowCustomUser.getName()));
                    }
                }
            }
        }
        return Response.ok(mixed);
    }

    @ApiOperation(value = "当前值班人员", notes = "部门字段list格式：[部门id, 地点id, 类型(operation_staff_type)]")
    @PostMapping(value = {"shift"+handler, "shift"+submitter})
    public Response<List<WorkflowUser>> shift(@RequestBody SubmitterParam submitterParam, @RequestParam(required = false) String condition) {
        if (condition != null && !allowCondition(submitterParam.getFormVariables(), condition)) {
            return Response.ok(null);
        }
        // 解析部门表达式
        List<WorkflowUser> users = submitterParam.getUsers();
        Assert.notNull(users, "插件配置不能为空人员");
        // 拿到所有的部门
        List<WorkflowUser> deptList = users.stream().filter(e -> e.getType() == 1).collect(Collectors.toList());
        Assert.isTrue(deptList.size() == 3, "部门字段list格式：[部门id, 地点id, 类型]");
        List<WorkflowUser> newDeptList = new ArrayList<>();
        for (WorkflowUser workflowUser : deptList) {
            Function<String, List<WorkflowUser>> function = DeptMarkEnum.getByMark(workflowUser.getName());
            List<WorkflowUser> apply = function.apply(submitterParam.getFormVariables());
            if (apply == null) {
                // null表示没有变量，正常使用即可
                newDeptList.add(workflowUser);
            } else {
                // 有变量
                newDeptList.addAll(apply);
            }
        }
        // 查询当前值班人员
        String deptId = newDeptList.get(0).getName();
        String locationId = newDeptList.get(1).getName();
        String staffType = newDeptList.get(2).getName();
        ShiftRecordQuery shiftRecordQuery = new ShiftRecordQuery();
        shiftRecordQuery.setDeptId(deptId);
        shiftRecordQuery.setLocationId(locationId);
        shiftRecordQuery.setStaffTypes(Collections.singletonList(staffType));
        Response<ChangeShiftDTO> info = shiftFeign.info(shiftRecordQuery);
        ClassesShiftDTO thisClassesShift = info.getData().getThisClassesShift();
        Assert.isTrue(thisClassesShift != null && CollectionUtils.isNotEmpty(thisClassesShift.getStaffs()), "未找到当前值班人员");
        List<WorkflowUser> result = thisClassesShift.getStaffs()
                .stream()
                .map(e -> new WorkflowUser(e.getStaffBy(), e.getStaffName()))
                .collect(Collectors.toList());
        return Response.ok(result);
    }
}
